package com.xingchi.tornado.mybatisplus.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.xingchi.tornado.constant.FieldConstants;
import com.xingchi.tornado.unique.provider.impl.SnowflakeProvider;
import org.apache.ibatis.reflection.MetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.Objects;

/**
 * MyBatis-Plus元数据对象处理器
 * 用于自动填充实体类的审计字段
 *
 * @author xingchi
 * @date 2023/5/21 20:51
 * @modified xingchi
 */
public class MyBatisPlusMetaObjectHandler implements MetaObjectHandler {

    private static final Logger log = LoggerFactory.getLogger(MyBatisPlusMetaObjectHandler.class);

    /**
     * String类型全类名
     */
    private static final String STRING_CLASS_NAME = "java.lang.String";

    /**
     * Long类型全类名
     */
    private static final String LONG_CLASS_NAME = "java.lang.Long";

    private final SnowflakeProvider snowflakeProvider;

    /**
     * 构造函数
     *
     * @param snowflakeProvider 雪花ID生成器
     */
    public MyBatisPlusMetaObjectHandler(SnowflakeProvider snowflakeProvider) {
        this.snowflakeProvider = snowflakeProvider;
    }

    /**
     * 插入操作的填充策略
     *
     * @param metaObject 元数据对象
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        try {
            // 设置ID
            this.strictInsertFill(metaObject, FieldConstants.ID, Long.class, snowflakeProvider.nextId());

            // 设置创建和更新时间
            LocalDateTime currentTime = LocalDateTime.now();
            if (metaObject.hasSetter(FieldConstants.CREATE_TIME)) {
                this.strictInsertFill(metaObject, FieldConstants.CREATE_TIME, LocalDateTime.class, currentTime);
            }

            if (metaObject.hasSetter(FieldConstants.UPDATE_TIME)) {
                this.strictInsertFill(metaObject, FieldConstants.UPDATE_TIME, LocalDateTime.class, currentTime);
            }

            // 设置创建人和更新人
            fillUserInfo(metaObject, true);
        } catch (Exception e) {
            log.error("MyBatis-Plus自动填充异常 - 插入操作", e);
        }
    }

    /**
     * 更新操作的填充策略
     *
     * @param metaObject 元数据对象
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        try {
            // 设置更新时间
            if (metaObject.hasSetter(FieldConstants.UPDATE_TIME)) {
                this.strictInsertFill(metaObject, FieldConstants.UPDATE_TIME, LocalDateTime.class, LocalDateTime.now());
            }

            // 设置更新人
            fillUserInfo(metaObject, false);
        } catch (Exception e) {
            log.error("MyBatis-Plus自动填充异常 - 更新操作", e);
        }
    }

    /**
     * 填充用户信息（创建人或更新人）
     *
     * @param metaObject 元数据对象
     * @param isInsert 是否为插入操作
     */
    private void fillUserInfo(MetaObject metaObject, boolean isInsert) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            return;
        }

        Object principal = authentication.getPrincipal();
        // 查询当前用户信息的id字段
        Field idField = ReflectionUtils.findField(principal.getClass(), FieldConstants.ID);

        // 如果不存在id字段，则不进行填充
        if (Objects.isNull(idField)) {
            return;
        }

        ReflectionUtils.makeAccessible(idField);
        Class<?> fieldType = idField.getType();

        try {
            // 处理String类型的用户ID
            if (STRING_CLASS_NAME.equals(fieldType.getName())) {
                handleStringTypeUserID(metaObject, principal, idField, isInsert);
            }
            // 处理Long类型的用户ID
            else if (LONG_CLASS_NAME.equals(fieldType.getName())) {
                handleLongTypeUserID(metaObject, principal, idField, isInsert);
            }
        } catch (IllegalAccessException e) {
            log.warn("无法访问用户ID字段", e);
        }
    }

    /**
     * 处理String类型的用户ID
     *
     * @param metaObject 元数据对象
     * @param principal 当前用户主体
     * @param idField ID字段
     * @param isInsert 是否为插入操作
     * @throws IllegalAccessException 如果访问字段失败
     */
    private void handleStringTypeUserID(MetaObject metaObject, Object principal, Field idField, boolean isInsert)
            throws IllegalAccessException {

        String principalId = (String) idField.get(principal);

        if (isInsert && metaObject.hasSetter(FieldConstants.CREATE_BY)) {
                this.strictInsertFill(metaObject, FieldConstants.CREATE_BY, String.class, principalId);
            }


        if (metaObject.hasSetter(FieldConstants.UPDATE_BY)) {
            this.strictInsertFill(metaObject, FieldConstants.UPDATE_BY, String.class, principalId);
        }
    }

    /**
     * 处理Long类型的用户ID
     *
     * @param metaObject 元数据对象
     * @param principal 当前用户主体
     * @param idField ID字段
     * @param isInsert 是否为插入操作
     * @throws IllegalAccessException 如果访问字段失败
     */
    private void handleLongTypeUserID(MetaObject metaObject, Object principal, Field idField, boolean isInsert)
            throws IllegalAccessException {

        Long principalId = (Long) idField.get(principal);

        if (isInsert && metaObject.hasSetter(FieldConstants.CREATE_BY)) {
                this.strictInsertFill(metaObject, FieldConstants.CREATE_BY, Long.class, principalId);
            }


        if (metaObject.hasSetter(FieldConstants.UPDATE_BY)) {
            this.strictInsertFill(metaObject, FieldConstants.UPDATE_BY, Long.class, principalId);
        }
    }
}
